package com.own.component.token.base;

import com.own.component.common.exception.BusinessSimpleException;
import com.own.component.store.core.method.StoreSet;
import com.own.component.store.core.method.StoreValue;
import com.own.component.token.config.TokenInstance;
import com.own.component.token.core.TokenRuleManager;
import lombok.extern.slf4j.Slf4j;

import java.util.Set;

/**
 * AbstractStoreTokenManager
 * <p>
 * 会造成这个token失效了，但是userId的缓存下仍然存在
 *
 * @author chenxueli
 * @date 2023-11-07 19:00:02
 */
@Slf4j
public abstract class AbstractStoreTokenManager implements StoreTokenManager {

    /**
     * 存放token信息的容器（key为用户id参数）
     */
    private final StoreSet<String> TOKEN_CONTAINER;
    /**
     * 存放userId的容器（key为登录token参数）
     */
    private final StoreValue<String> USER_ID_CONTAINER;
    /**
     * token生成策略
     */
    private final TokenBuildPolicy TOKEN_BUILD_POLICY;

    public AbstractStoreTokenManager(
            StoreSet<String> tokenContainer,
            StoreValue<String> userIdContainer,
            TokenBuildPolicy tokenBuildPolicy
    ) {
        this.TOKEN_CONTAINER = tokenContainer;
        this.USER_ID_CONTAINER = userIdContainer;
        this.TOKEN_BUILD_POLICY = tokenBuildPolicy;
    }

    /**
     * 获取客户端id
     *
     * @return 客户端id
     */
    @Override
    public String getDefaultClient() {
        return TokenInstance.INSTANCE.client();
    }

    /**
     * 创建一个token
     *
     * @param userId 用户id
     * @param client 客户端id
     * @return 生成的登录令牌
     */
    @Override
    public String createToken(Long userId, String client) {
        return createToken(userId, client, TOKEN_BUILD_POLICY);
    }

    /**
     * 根据用户id，客户端id获取token
     *
     * @param userId           用户id
     * @param client           客户端id
     * @param tokenBuildPolicy 令牌生成策略
     * @return 生成的登录令牌
     */
    @Override
    public String createToken(Long userId, String client, TokenBuildPolicy tokenBuildPolicy) {
        // 获取对应的容器key
        var tokenContainerKey = buildTokenContainerKey(userId, client);
        // 获取对应的匹配规则
        var rule = TokenRuleManager.get(client);
        // 执行判断，token的容器中有没有tokenContainerKey
        if (TOKEN_CONTAINER.contains(tokenContainerKey)) {
            // 规则校验判断
            switch (rule.getPolicy()) {
                // 公用（如果是共存的当前token有且仅存在一个）
                case PUBLIC -> {
                    // 获取所有的token信息
                    var tokenList = TOKEN_CONTAINER.members(tokenContainerKey);
                    // 如果token信息为空，执行构造逻辑
                    if (tokenList.isEmpty()) {
                        break;
                    }
                    // 如果token信息不为空，获取第一个token信息
                    return tokenList.stream().findFirst().orElse(null);
                }
                // 共存（同时存在使用不同的token）
                case COEXIST_DIFFERENT -> {
                    // tips 每次登录都是一个新的token
                }
                // 覆盖（全局唯一，删除已有的所有同账号同客户端的登录信息）
                case OVERRIDE_GLOBAL -> deleteAllByUserId(userId);
                // 覆盖（客户端唯一，删除已有的所有同账号端的登录信息）
                case OVERRIDE_CLIENT -> deleteByUserId(userId);
                default -> deleteByUserId(userId);
            }
        }
        // 根据token策略生成新的token
        var token = cycleBuildToken(userId, client, tokenBuildPolicy, 0, 1);
        var userIdContainerKey = buildUserIdContainerKey(token, client);
        // 将token信息存入缓存
        TOKEN_CONTAINER.add(tokenContainerKey, token);
        TOKEN_CONTAINER.expire(tokenContainerKey, rule.getTimeout());
        USER_ID_CONTAINER.set(userIdContainerKey, String.valueOf(userId), rule.getTimeout());
        return token;
    }

    /**
     * 检查 token 是否有效
     *
     * @param token  登录令牌
     * @param client 客户端id
     * @return 是否有效
     */
    @Override
    public boolean checkToken(String token, String client) {
        // 获取用户id容器（key是token）
        var userIdContainerKey = buildUserIdContainerKey(token, client);
        return USER_ID_CONTAINER.contains(userIdContainerKey);
    }

    /**
     * 获取token
     *
     * @param userId 用户id
     * @param client 客户端id
     * @return 登录令牌列表
     */
    @Override
    public Set<String> listToken(Long userId, String client) {
        var tokenContainerKey = buildTokenContainerKey(userId, client);
        return TOKEN_CONTAINER.members(tokenContainerKey);
    }

    /**
     * 获取token
     *
     * @param token  登录令牌
     * @param client 客户端id
     * @return 用户id
     */
    @Override
    public Long getUserId(String token, String client) {
        var userIdContainerKey = buildUserIdContainerKey(token, client);
        var userId = USER_ID_CONTAINER.get(userIdContainerKey);
        // 续签token
        if (userId != null) {
            // 获取对应的匹配规则
            var rule = TokenRuleManager.get(client);
            var tokenContainerKey = buildTokenContainerKey(Long.valueOf(userId), client);
            TOKEN_CONTAINER.expire(tokenContainerKey, rule.getTimeout());
            USER_ID_CONTAINER.set(userIdContainerKey, userId, rule.getTimeout());
            return Long.valueOf(userId);
        }
        return null;
    }

    /**
     * 清除 token
     *
     * @param token  登录令牌
     * @param client 客户端id
     */
    @Override
    public void deleteByToken(String token, String client) {
        var userIdContainerKey = buildUserIdContainerKey(token, client);
        var userId = USER_ID_CONTAINER.get(userIdContainerKey);
        var tokenContainerKey = buildTokenContainerKey(userId == null ? null : Long.valueOf(userId), client);
        TOKEN_CONTAINER.remove(tokenContainerKey);
        USER_ID_CONTAINER.remove(userIdContainerKey);
    }

    /**
     * 清除 token（会删除这个用户的所有token信息）
     *
     * @param userId 用户id
     * @param client 客户端id
     */
    @Override
    public void deleteByUserId(Long userId, String client) {
        var tokenContainerKey = buildTokenContainerKey(userId, client);
        // 获取所有的token信息
        var tokenList = TOKEN_CONTAINER.members(tokenContainerKey);
        // 删除所有的token信息
        TOKEN_CONTAINER.remove(tokenContainerKey);
        tokenList.forEach(token -> USER_ID_CONTAINER.remove(buildUserIdContainerKey(token, client)));
    }

    /**
     * 清除 token（会删除这个用户的所有token信息）
     *
     * @param userId 用户id
     */
    @Override
    public void deleteAllByUserId(Long userId) {
        var clientList = TokenRuleManager.clientArray();
        clientList.forEach(client -> {
            var tokenContainerKey = buildTokenContainerKey(userId, client);
            // 获取所有的token信息
            var tokenList = TOKEN_CONTAINER.members(tokenContainerKey);
            // 删除所有的token信息
            TOKEN_CONTAINER.remove(tokenContainerKey);
            tokenList.forEach(token -> USER_ID_CONTAINER.remove(buildUserIdContainerKey(token, client)));
        });
    }

    /**
     * 循环生成token
     *
     * @param userId           用户id
     * @param client           客户端id
     * @param tokenBuildPolicy 令牌生成策略
     * @param count            当前次数
     * @param max              最大次数
     * @return 生成的token
     */
    private String cycleBuildToken(Long userId, String client, TokenBuildPolicy tokenBuildPolicy, int count, int max) {
        if (count > max) {
            throw new BusinessSimpleException("token生成次数较多，生成失败");
        }
        // 根据token策略生成新的token
        var token = tokenBuildPolicy.build(userId, client);
        // 这个token需要是不存在的，不然会再次生成如果连续max次生成的token相同，返回异常信息
        var userIdContainerKey = buildUserIdContainerKey(token, client);
        if (USER_ID_CONTAINER.contains(userIdContainerKey)) {
            return cycleBuildToken(userId, client, tokenBuildPolicy, count + 1, max);
        }
        return token;
    }

}
